#pragma once 
#include <iostream>
#include <functional>
#include <unordered_map>
#include <string>
#include <cassert>
#include "Err.hpp"
#include "Log.hpp"
#include "Sock.hpp"
#include "Epoll.hpp"
#include "Util.hpp"
#include "protocol.hpp"
namespace tcp_server
{
    class Connection;
    class TcpServer;
    static const uint16_t defaultPort = 8080;
    static const int num = 64;//表示最多可以存储多少个就绪事件
    static const int timeout = 1000;//超时时间
    using func_t = std::function<void (Connection*)>;//三种回调方法，读就绪，写就绪，异常就绪
    //using hander_t = std::function<void(const std::string&)>;
    class Connection//每一个套接字都要有自己的缓冲区（把每一个套接字看成Connection对象）
    {
    public:
        Connection(int sock, TcpServer* pTS)
            :_sock(sock)
            ,_pTS(pTS)
        {}
        ~Connection(){}
    public:
        void Register(func_t readFunc, func_t writeFunc, func_t errFunc)//注册事件
        {
            _recver = readFunc;
            _sender = writeFunc;
            _excepter = errFunc;
        }
        void Close()
        {
            close(_sock);
        }
    public:
        int _sock;
        std::string _inBuffer;//输入缓冲区。注意图片和视频的传输格式,每个对象一个缓冲区，考科一防止数据读一半的情况
        std::string _outBuffer;//输出缓冲区
        func_t _recver;//从sock中读
        func_t _sender;//向sock中写
        func_t _excepter;//处理sock在io时的异常事件
        TcpServer* _pTS;//tcpServer的指针,用于外部调用Connection对象可以控制TcpServer中的EnableReadWrite()接口
        uint64_t lastTime;//最近一次访问时间，每一次读和写都更新一下时间
    };

    class TcpServer//Reactor
    {
    public:
        TcpServer(func_t func, uint16_t port = defaultPort)
            :_service(func)
            ,_port(port) 
            ,_revs(nullptr)
        {}
        ~TcpServer()
        {
            _sock.Close();
            _epoll.Close();
            if(nullptr != _revs) delete[] _revs;//还有unordered_map没有析构
        }
    public:
        void InitServer()
        {
            //1、创建socket
            _sock.Socket();
            _sock.Bind(_port);
            _sock.Listen();
            //构建epoll对象
            _epoll.Create();
            //将listen套接字添加到epoll模型中
            AddConnnection(_sock.GetListenSocket(), EPOLLIN | EPOLLET, 
                            std::bind(&TcpServer::Accept, this, std::placeholders::_1), nullptr, nullptr);
            _revs = new struct epoll_event[num];
            _num = num;
        }
        void EnableReadWrite(Connection* conn, bool readAble, bool writeAble)//使能读、写
        {
            uint32_t event = (readAble ? EPOLLIN : 0) | (writeAble ? EPOLLOUT : 0) | EPOLLET;
            _epoll.Control(conn->_sock, event, EPOLL_CTL_MOD); 
        }
        void Dispatch()//事件派发
        {
            while(1)
            {
                Loop(timeout);
                //所有事情做完后，遍历所有的连接，计算每一个连接已经多久没发消息了，现在时间和lastTime相减，超过5分钟就关闭连接
            }
        }
    private:
        void Accept(Connection* conn)//监听事件的回调函数
        {
            //获取新连接,监听套接字也是非阻塞的。
            //Accept在非阻塞模式，返回值为-1时，判断errno即可知道是否读到所有的新连接
            while(1)
            {
                std::string clientIp;
                uint16_t clientPort;
                int err = 0;//用于提取Accept的返回值
                int sock = _sock.Accept(&clientIp, &clientPort, &err);
                if(sock >= 0)
                {
                    AddConnnection(sock, EPOLLIN | EPOLLET, 
                                std::bind(&TcpServer::Read, this, std::placeholders::_1), 
                                std::bind(&TcpServer::Write, this, std::placeholders::_1), 
                                std::bind(&TcpServer::Except, this, std::placeholders::_1));
                    LogMessage(DEBUG, "git a new link, info: [%s:%d]", clientIp.c_str(), clientPort);
                }
                else
                {
                    if(err == EAGAIN || err == EWOULDBLOCK) break;//次数说明Accept把文件描述符全部读完了
                    else if(err == EINTR) continue;//信号中断
                    else 
                    {
                        break;//Accept出错了
                    }
                }
            }
        }
        void Read(Connection* conn)//普通读事件的回调
        {
            conn->lastTime = time(nullptr);
            char buffer[1024];
            while(1)
            {
                ssize_t s = recv(conn->_sock, buffer, sizeof(buffer)-1, 0);
                if (s > 0)
                {
                    buffer[s] = 0;
                    conn->_inBuffer += buffer;//将读到的数据存入string
                    _service(conn);//对读取到的数据进行处理
                }
                else if (s == 0)//对端关闭连接
                {
                    if (conn->_excepter)//conn将会被释放，后续代码就不要操作conn指针了
                    {
                        conn->_excepter(conn);
                        return;
                    }
                }
                else//判断几种读取出异常的情况
                {
                    if(errno == EINTR) continue;
                    else if(errno == EAGAIN || errno == EWOULDBLOCK) break;
                    else
                    {
                        if(conn->_excepter)
                        {
                            conn->_excepter(conn);
                            return;
                        }
                    }
                }
            }
        }
        void Write(Connection* conn)//普通写事件的回调
        {
            conn->lastTime = time(nullptr);
            while(1)
            {
                ssize_t s = send(conn->_sock, conn->_outBuffer.c_str(), sizeof(conn->_outBuffer.size()), 0);
                if (s > 0)
                {
                    if (conn->_outBuffer.empty()) 
                    { 
                        //EnableReadWrite(conn, true, false);//写事件写完了就关掉
                        break; 
                    }
                    else
                    {
                        conn->_outBuffer.erase(0, s);
                    }
                }
                else
                {
                    if (errno == EAGAIN || errno ==EWOULDBLOCK) { break; }
                    else if (errno == EINTR) { continue; }
                    else
                    {
                        if (conn->_excepter)
                        {
                            conn->_excepter(conn);
                            return;
                        }
                    }
                }
            } 
            if (!conn->_outBuffer.empty())//如果没发完
            {
                conn->_pTS->EnableReadWrite(conn, true, true);
            }
            else//如果发完了
            {
                conn->_pTS->EnableReadWrite(conn, true, false);
            }
        }
        void Except(Connection* conn)//异常事件的回调
        {
            LogMessage(DEBUG, "Except");
            _epoll.Control(conn->_sock, 0, EPOLL_CTL_DEL);//在del的时候不关心是何种事件，有fd即可
            conn->Close();//关闭套接字
            _connections.erase(conn->_sock);
            delete conn;
        }
        void AddConnnection(int sock, uint32_t events, func_t readFunc, func_t writeFunc, func_t errFunc)//添加连接
        {
            //1、为该sock创建connection并初始化后添加到_connections
            if(events & EPOLLET)
            {
                Util::SetNonBlock(sock);//将监听套接字设置为非阻塞
            }
            Connection* conn = new Connection(sock, this);//构建Connection对象
            //2、给对应的sock设置对应的回调方法
            conn->Register(readFunc, writeFunc, errFunc);
            //3、将sock与它所关心的事件注册到epoll中
            bool r = _epoll.AddEvent(sock, events);
            assert(r); 
            (void)r;
            //4、将k、v添加到_connection中
            _connections.insert(std::pair<int, Connection*>(sock, conn));
            LogMessage(DEBUG, "add new sock : %d in epoll and unordered_map", sock);
        }
        void Loop(int timeout)//事件派发中的循环函数
        {
            int n = _epoll.Wait(_revs, _num, timeout);//捞出就绪事件的_revs
            for(int i = 0; i < n; ++i)
            {
                //通过_revs获得已就绪的fd和就绪事件
                int sock = _revs[i].data.fd;
                uint32_t events = _revs[i].events;
                //将异常问题，全部转化为读写问题，因为在读写时，读写接口自带读写问题的异常处理方式
                if((events & EPOLLERR)) events |= (EPOLLIN | EPOLLOUT);
                if((events & EPOLLHUP)) events |= (EPOLLIN | EPOLLOUT);//对端关闭连接
                if((events & EPOLLIN) && IsConnectionExist(sock))//监听事件及其他读事件就绪,保险起见，先判断connect对象是否存在
                {
                    if(_connections[sock]->_recver)//检查存在，防止空指针
                        _connections[sock]->_recver(_connections[sock]);//从map中找到key值为sock的Connection对象
                }
                if((events & EPOLLOUT) && IsConnectionExist(sock))
                {
                    if(_connections[sock]->_sender)//检查存在，防止空指针
                        _connections[sock]->_sender(_connections[sock]);
                }
            }
        }
        bool IsConnectionExist(int sock)
        {
            auto iter = _connections.find(sock);
            return iter != _connections.end();
        }
    private:
        uint16_t _port;
        Sock _sock;//里面包含有listenS  ocket
        Epoll _epoll;
        std::unordered_map<int, Connection*> _connections;//fd和Connection* 
        struct epoll_event* _revs;//捞出就绪的事件及其fd的数组，epoll_wait会去捞
        int _num;//表示最多可以存储多少个就绪事件
        // hander_t _handler;//解协议
        func_t _service;
    };
}